package com.ruubypay.web.core.shiro.utils;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.SerializationUtils;

import java.util.*;

/**
 * Desc : redis 缓存
 * @author sucyun 
 * Date :2018/4/4 下午11:17
 */
public class RedisCache<K, V> implements Cache<K, V> {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * redis Dao
     */
    private RedisDao redisDao;

    /**
     * shiro Session key前置字符串
     */
    private String keyPrefix = "shiro_redis_session:";

    public String getKeyPrefix() {
        return keyPrefix;
    }

    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }

    /**
     * 构造函数
     * @param redisDao redis 数据操作
     */
    public RedisCache(RedisDao redisDao) {
        if (redisDao == null) {
            throw new IllegalArgumentException("Cache argument cannot be null.");
        }
        this.redisDao = redisDao;
    }

    /**
     * 构造函数
     * @param redisDao redis 数据操作
     * @param prefix 前置字符串
     */
    public RedisCache(RedisDao redisDao, String prefix) {
        this(redisDao);
        this.keyPrefix = prefix;
    }

    /**
     * Desc : 获得byte[]型的key
     * @param key key
     * @return 返回序列化的key
     */
    private byte[] getByteKey(K key) {
        if (key instanceof String) {
            String preKey = this.keyPrefix + key;
            return preKey.getBytes();
        } else {
            return SerializationUtils.serialize(key);
        }
    }

    /**
     * Desc : 获取value
     * @author sucyun
     * @param key key
     * @return value
     * Date :2018/4/4 下午11:27
     */
    @Override
    public V get(K key) throws CacheException {
        logger.debug("根据key从Redis中获取对象 key [ {} ]",key);
        try {
            if (key == null) {
                return null;
            } else {
                byte[] rawValue = redisDao.getByte(key.toString());
                @SuppressWarnings("unchecked")
                V value = (V) SerializationUtils.deserialize(rawValue);
                return value;
            }
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * Desc : 添加redis
     * @author sucyun 
     * @param key 新增key
     * @param value 新增value
     * @return 新增value
     * Date :2018/4/4 下午11:27
     */
    @Override
    public V put(K key, V value) throws CacheException {
        logger.debug("存储 key [ {} ]",key);
        try {
            redisDao.set(key.toString(), SerializationUtils.serialize(value));
            return value;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * Desc : 返回删除对象
     * @author sucyun 
     * @param key 删除key
     * @return 删除对象
     * Date :2018/4/4 下午11:26
     */
    @Override
    public V remove(K key) throws CacheException {
        logger.debug("从redis中删除 key [" + key + "]");
        try {
            V previous = get(key);
            redisDao.delete(key.toString());
            return previous;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * Desc : 清空redis
     * @author sucyun
     * Date :2018/4/4 下午11:26
     */
    @Override
    public void clear() throws CacheException {
        logger.debug("从redis中删除所有元素");
        try {
            redisDao.flushDB();
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * Desc : 获取redis数据长度
     * @author sucyun
     * @return redis数据长度
     * Date :2018/4/4 下午11:24
     */
    @Override
    public int size() {
        try {
            Long longSize = redisDao.dbSize();
            return longSize.intValue();
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * Desc : 获取redis key集合
     * @author sucyun 
     * @return redis key集合
     * Date :2018/4/4 下午11:24
     */
    @SuppressWarnings("unchecked")
    @Override
    public Set<K> keys() {
        try {
            Set<String> keys = redisDao.keys(this.keyPrefix + "*");
            if (CollectionUtils.isEmpty(keys)) {
                return Collections.emptySet();
            } else {
                Set<K> newKeys = new HashSet<K>();
                for (String key : keys) {
                    newKeys.add((K) key);
                }
                return newKeys;
            }
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * Desc : 获取redis value集合
     * @author sucyun
     * @return redis value集合
     * Date :2018/4/4 下午11:24
     */
    @Override
    public Collection<V> values() {
        try {
            Set<String> keys = redisDao.keys(this.keyPrefix + "*");
            if (!CollectionUtils.isEmpty(keys)) {
                List<V> values = new ArrayList<V>(keys.size());
                for (String key : keys) {
                    @SuppressWarnings("unchecked")
                    V value = get((K) key);
                    if (value != null) {
                        values.add(value);
                    }
                }
                return Collections.unmodifiableList(values);
            } else {
                return Collections.emptyList();
            }
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }
}
